package com.ideal.manage.esms.service.monitor;

import com.alibaba.fastjson.JSONObject;
import com.ideal.manage.esms.bean.esms.EsmsDataBatch;
import com.ideal.manage.esms.bean.esms.EsmsIntegration;
import com.ideal.manage.esms.bean.esms.QEsmsDataBatch;
import com.ideal.manage.esms.bean.project.*;
import com.ideal.manage.esms.jpa.JpaRepositoryImpl;
import com.ideal.manage.esms.repository.programme.ParameterInfoRepository;
import com.ideal.manage.esms.service.ElasticsearchService;
import com.ideal.manage.esms.service.IntegrationService;
import com.ideal.manage.esms.util.ElasticsearchUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.joda.time.format.DateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.math.BigDecimal;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class MonitorHistoryDataPcsService {

    @Autowired
    private EntityManager entityManager;
    private JpaRepositoryImpl<ParameterInfo, String> parameterJpaRepository;
    private JpaRepositoryImpl<EquipmentManage, String> equipmentManageRepository;
    private JpaRepositoryImpl<ProjectManage, String> projectManageRepository;
    private JpaRepositoryImpl<EsmsIntegration, Long> integrationRepository;
    private JpaRepositoryImpl<EsmsDataBatch, Long> dataBatchRepository;
    @Autowired
    private IntegrationService integrationService;
    @Autowired
    private TransportClient transportClient;

    @Autowired
    private ElasticsearchService elasticsearchService;

    @Autowired
    private ParameterInfoRepository parameterInfoRepository;

    private static final Logger logger = LoggerFactory.getLogger(ElasticsearchUtils.class);

    @PostConstruct
    public void init() {
        this.parameterJpaRepository = new JpaRepositoryImpl<>(ParameterInfo.class, entityManager);
        this.equipmentManageRepository = new JpaRepositoryImpl<>(EquipmentManage.class, entityManager);
        this.projectManageRepository = new JpaRepositoryImpl<>(ProjectManage.class, entityManager);
        this.integrationRepository = new JpaRepositoryImpl<EsmsIntegration, Long>(EsmsIntegration.class, entityManager);
        this.dataBatchRepository = new JpaRepositoryImpl<EsmsDataBatch, Long>(EsmsDataBatch.class, entityManager);
    }

    /**
     * 历史数据
     *
     * @param id        设备id
     * @param objType   测点类别
     * @param name      测点名称
     * @param ranges    时间范围
     * @param frequency 显示密度 小时 15分钟
     * @param showType  显示类别 //平均值，最大值 最小值 当前无效
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return
     */
    public JSONObject historyDataEquipmentPcs(String id, int objType, String name, String ranges,
                                              String frequency, String showType, String startTime, String endTime) {
        JSONObject r = new JSONObject();
        // 2. 获取当前设备
        EquipmentManage projectManage = equipmentManageRepository.findOne(id);
        if (projectManage == null) {
            return r;
        }
        // 4. 获取时间
        // 4. 获取时间
        Tuple<LocalDateTime, LocalDateTime> dateRange = Utils.calDateRange(ranges, startTime, endTime);
        if (dateRange == null) {
            return r;
        }
        String title = String.format("%s %s-%s", projectManage.getName(),
                dateRange.v1().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
                dateRange.v2().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        r.put("title", title);
        Date startDate = Date.from(dateRange.v1().toInstant(ZoneOffset.of("+8")));
        Date endDate = Date.from(dateRange.v2().toInstant(ZoneOffset.of("+8")));
        switch (objType) {
            case 1:
                //电压
                r.putAll(getHistoryElectricQuantity(projectManage.getProject().getIp(),projectManage.getCode(), name, ranges, frequency, showType, startDate, endDate));
                break;
            case 2:
                //能量
                r.putAll(getHistoryEnergy(projectManage.getProject().getIp(),name, ranges, frequency, startDate, endDate));
                break;
            case 3:
                //电流
                r.putAll(getHistoryEfficiency(projectManage.getProject().getIp(),projectManage.getCode(),name, ranges, frequency, showType, startDate, endDate));
                break;
            case 4:
                //温度
                r.putAll(getHistoryEfficiency(projectManage.getProject().getIp(),projectManage.getCode(),name, ranges, frequency, showType, startDate, endDate));
                break;
            case 5:
                //功率因素特殊计算
                if("功率因数".equals(name)){
                    //初始化-计算-功率因数
                    JSONObject glysJson = new JSONObject();
                    Double ygglVal = null;
                    Double wgglVal = null;
                    JSONObject yggl = getHistoryEfficiency(projectManage.getProject().getIp(),projectManage.getCode(),"有功功率", ranges, frequency, showType, startDate, endDate);
                    JSONObject wggl = getHistoryEfficiency(projectManage.getProject().getIp(),projectManage.getCode(),"无功功率", ranges, frequency, showType, startDate, endDate);
                    for (String key : yggl.keySet()) {
                        ygglVal = NumberUtils.toDouble(yggl.get(key).toString(), 0.0);
                        wgglVal = NumberUtils.toDouble(wggl.get(key).toString(), 0.0);
                        if(Math.sqrt(Math.pow(ygglVal,2)+Math.pow(wgglVal,2)) != 0){
                            glysJson.put(key,ygglVal / Math.sqrt(Math.pow(ygglVal,2)+Math.pow(wgglVal,2)));
                        }else{
                            glysJson.put(key,0.0);
                        }
                        logger.info("有功功率:"+ygglVal +"   无功功率: "+wgglVal);
                    }
                    r.putAll(glysJson);
                }else {
                    r.putAll(getHistoryEfficiency(projectManage.getProject().getIp(),projectManage.getCode(),name, ranges, frequency, showType, startDate, endDate));
                }
                break;
        }
        return r;
    }

    /**
     *
     * @param ip
     * @param code
     * @param name
     * @param ranges
     * @param frequency
     * @param showType
     * @param startDate
     * @param endDate
     * @return
     */
    protected JSONObject queryModbus(String ip, String code, String name, String ranges, String frequency, String showType, Date startDate, Date endDate) {
        Map<String, Integer> nameMapping = new HashMap<>();
        //电压
        nameMapping.put("AB线电压", 2000);
        nameMapping.put("BC线电压", 2001);
        nameMapping.put("CA线电压", 2002);
        nameMapping.put("直流母线电压", 2009);

        //电流
        nameMapping.put("A相电流", 2003);
        nameMapping.put("B相电流", 2004);
        nameMapping.put("C相电流", 2005);
        nameMapping.put("直流母线电流", 2010);

        //温度
        nameMapping.put("A相IGBT温度", 2015);
        nameMapping.put("B相IGBT温度", 2016);
        nameMapping.put("C相IGBT温度", 2017);

        //功率
        nameMapping.put("有功功率", 2007);
        nameMapping.put("无功功率", 2008);
        //nameMapping.put("功率因数", "glys");
        nameMapping.put("直流功率", 2011);

        Integer typeId = nameMapping.getOrDefault(name, 0);
        Double xishu = 1.0;
        ChartUtils.AggregationCondition condition = ChartUtils.buildCondition(frequency, ranges);
        JSONObject r = condition.defaultData;
        if (typeId == 0) {
            return r;
        }
        if (2000<=typeId &&typeId<=2014) {
            xishu = 0.1;
        }
        ESValueSourceAggregation aggregationBuilder = ChartUtils.getAggregation(showType);
        if (aggregationBuilder == null) {
            return r;
        }

        elasticsearchService.aggregation(QueryBuilders.boolQuery()
                        .filter(QueryBuilders.termQuery("data_source", "mdbs"))
                        .filter(QueryBuilders.termQuery("ip",ip))
                        .filter(QueryBuilders.termQuery("macid",code))
                        .filter(QueryBuilders.existsQuery(String.valueOf(typeId)))
                        .filter(QueryBuilders.rangeQuery("time").gte(startDate.getTime()).lte(endDate.getTime())),
                condition.interval, aggregationBuilder, (filedName, time, val) -> {
                    r.put(DateTimeFormat.forPattern(condition.fmt).print(time), val);
                }, String.valueOf(typeId));
        for(String s : r.keySet()){
            Double dval = Double.parseDouble(r.get(s).toString())*xishu;
            logger.info(r.get(s)+"                "+xishu+"      "+dval);
            r.put(s,String.valueOf(dval));
        }
        return r;
    }

    /**
     *
     * @param ip
     * @param code
     * @param ranges
     * @param frequency
     * @param showType
     * @param startDate
     * @param endDate
     * @return
     */
    protected JSONObject getHistoryEfficiency(String ip,String code, String name, String ranges, String frequency, String showType, Date startDate, Date endDate) {
        return this.queryModbus(ip, code, name, ranges, frequency, showType, startDate, endDate);
    }

    /**
     * 积分数据
     *
     * @param name      测点名称
     * @param ranges    时间范围
     * @param frequency 时间类型
     * @param startDate 开始时间
     * @param endDate   结束时间
     * @return
     */
    protected JSONObject getHistoryEnergy(String ip,String name, String ranges, String frequency, Date startDate, Date endDate) {
        JSONObject r = new JSONObject();
        String sql = "SELECT DATE_FORMAT(`cal_time`, '%d %H:%i') AS time, sum(val)" +
                " FROM esms_integration WHERE type=?  AND cal_time BETWEEN ? AND ? AND project_ip =? ";
        switch (frequency) {
            case "15":
                r = Utils.getFifteenMinutes();
                break;
            case "hour":
                r = Utils.getHour();
                sql = "SELECT DATE_FORMAT(`cal_time`, '%Y-%m-%d %H') AS time, sum(val)" +
                        " FROM esms_integration WHERE type=? AND cal_time BETWEEN ? AND ?  AND project_ip =? GROUP BY time";
                break;
            case "day":
                r = Utils.getDay(ranges);
                sql = "SELECT DATE_FORMAT(`cal_time`, '%Y-%m-%d') AS time, sum(val)" +
                        " FROM esms_integration WHERE type=? AND cal_time BETWEEN ? AND ?  AND project_ip =? GROUP BY time";
                break;
            case "month":
                r = Utils.getMonth();
                sql = "SELECT DATE_FORMAT(`cal_time`, '%Y-%m') AS time, sum(val)" +
                        " FROM esms_integration WHERE type=? AND cal_time BETWEEN ? AND ? AND project_ip =? GROUP BY time";
                break;
            default:
                r = new JSONObject();
                return r;
        }

        Query qry = entityManager.createNativeQuery(sql);
        qry.setParameter(2, startDate);
        qry.setParameter(3, endDate);
        qry.setParameter(4, ip);
        switch (name) {
            case "充电量":
                qry.setParameter(1, "cdl");
                break;
            case "放电量":
                qry.setParameter(1, "fdl");
                break;
            case "总充电量":
                qry.setParameter(1, "dtcdl");
                break;
            case "总放电量":
                qry.setParameter(1, "dtfdl");
                break;
            default:
                return r;
        }
        List<Object[]> result = qry.getResultList();
        for (Object[] rs : result) {
            r.put((String) rs[0], rs[1]);
        }

        return r;

    }

    /**
     *
     * @param ip
     * @param code
     * @param name
     * @param ranges
     * @param frequency
     * @param showType
     * @param startDate
     * @param endDate
     * @return
     */
    protected JSONObject getHistoryElectricQuantity(String ip,String code, String name, String ranges, String frequency, String showType, Date startDate, Date endDate) {
        return this.queryModbus(ip, code, name, ranges, frequency, showType, startDate, endDate);
    }

    /**
     * 实时监控 -> 变流器监控
     *
     * @param equipmentType
     * @return
     */
    public List<ParameterInfo> findListByTypeForPcsMonitor(String equipmentType, EquipmentManage equipmentManage) {
        Integer parameterType = null;
        // 1. 获取所有 需要显示 的测点参数
        // 需要做这一步是因为 设备管理 表中的设备类型 和 测点参数 表中的设备类型值代表的意义不一致
        switch (equipmentType) {
            // 变流器
            case "1":
                parameterType = 4;
                break;
            default:
                return null;
        }
        List<ParameterInfo> list = parameterInfoRepository.findListByTypeForMonitor(parameterType);
        // 2. 把这些参数分类
        // 1) 实时  // 这个是实时的分组
        List<ParameterInfo> nowList =
                list.stream().filter(parameterInfo -> parameterInfo.getEsAggsMethod().toString().equals("3")).collect(Collectors.toList());
        // 3. 获取
        //将过滤后的数据(指定电池单体,实时的参数测点配置数据)
        //nowList = ElasticsearchUtils.getNowValue(nowList, equipmentManage);

        // 4. 返回
        return this.getValuePcs(nowList, equipmentManage);
    }

    /**
     * pcs实时监控数据
     * @param list
     * @param equipmentManage
     * @return
     */
    public List<ParameterInfo> getValuePcs(List<ParameterInfo> list, EquipmentManage equipmentManage) {
        QEsmsDataBatch qEsmsDataBatch = QEsmsDataBatch.esmsDataBatch;
        Long batchId = dataBatchRepository.query()
                .select(qEsmsDataBatch.id)
                .from(qEsmsDataBatch)
                .orderBy(qEsmsDataBatch.time.desc())
                .where(qEsmsDataBatch.projectIp.eq(equipmentManage.getProject().getIp())
                        .and(qEsmsDataBatch.type.eq("mdbs")))
                .limit(1)
                .fetchOne();
        if (batchId == null) {
            return list;
        }
        //初始化-计算-功率因数
        Double glys = null;
        Double yggl = null;
        Double wggl = null;

        //设置时间
        LocalDateTime startTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0, 0));
        Date end = Date.from(startTime.plusHours(23).withMinute(59).withSecond(59).toInstant(ZoneOffset.of("+8")));
        Date start = Date.from(startTime.toInstant(ZoneOffset.of("+8")));

        Map<String, ParameterInfo> keyAndValue = new HashMap<>();
        for (ParameterInfo parameterInfo : list) {
            if (NumberUtils.isDigits(parameterInfo.getEsName())) {
                keyAndValue.put(parameterInfo.getEsName(), parameterInfo);
            } else {
                //TODO 不能直接从ES中取的，请在这里做计算
                logger.info(parameterInfo.getEsName()+"   pcs不能直接从ES中取的计算 ");
                parameterInfo.setValue(this.transferPowerForDay(parameterInfo.getEsName()));
            }
        }

        //构造ES查询语句条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("time").gte(start.getTime()).lte(end.getTime()));
        //boolQueryBuilder.filter(QueryBuilders.termQuery("macid", equipmentManage.getCode()));
        //boolQueryBuilder.filter(QueryBuilders.termQuery("ip", equipmentManage.getProject().getIp()));  //batchId
        boolQueryBuilder.filter(QueryBuilders.termQuery("data_source", "mdbs"));
        boolQueryBuilder.filter(QueryBuilders.prefixQuery("id", batchId + "_"));

        SearchRequestBuilder prepareSearch = transportClient.prepareSearch();
        prepareSearch.setQuery(boolQueryBuilder);
        prepareSearch.setSize(1000);
        SearchResponse searchResponse = prepareSearch.execute().actionGet();
        if (searchResponse.getHits().getHits().length == 0) {
            return list;
        }
        for (SearchHit hit : searchResponse.getHits().getHits()) {
            Map<String, Object> hitSourceAsMap = hit.getSourceAsMap();
            for (String key : hitSourceAsMap.keySet()) {
                if (keyAndValue.containsKey(key)) {
                    logger.info(key+"    "+hitSourceAsMap.get(key).toString());
                    double val = NumberUtils.toDouble(hitSourceAsMap.get(key).toString(), 0.0);
                    if(2000<=Integer.parseInt(key)&&Integer.parseInt(key) <=2014){
                        keyAndValue.get(key).setValue(val*0.1);
                    }else{
                        keyAndValue.get(key).setValue(val);
                    }
                    if("2007".equals(key)){
                        yggl = val;
                    }
                    if("2008".equals(key)){
                        wggl = val;
                    }
                }
            }
        }

        //计算 glys 有功功率P除以[（有功功率P的平方＋无功功率Q的平方)再开平方]
        for(ParameterInfo parameterInfo : list){
            if("glys".equals(parameterInfo.getEsName())){
                if(Math.sqrt(Math.pow(yggl,2)+Math.pow(wggl,2)) != 0){
                    glys = yggl / Math.sqrt(Math.pow(yggl,2)+Math.pow(wggl,2));
                    logger.info("glys             : "+glys);
                    parameterInfo.setValue(glys);
                }
                break;
            }
        }

        //  返回
        return list;
    }

    /**
     * PCS-实时需要统计的积分数据
     * @param type
     * @return
     */
    public Double transferPowerForDay(String type) {
        org.elasticsearch.common.collect.Tuple<LocalDateTime, LocalDateTime> dateRange = Utils.calDateRange("day", null, null);
        Date startDate = Date.from(dateRange.v1().toInstant(ZoneOffset.of("+8")));
        Date endDate = Date.from(dateRange.v2().toInstant(ZoneOffset.of("+8")));
        String sql = "SELECT DATE_FORMAT(`cal_time`, '%Y-%m-%d') AS time, sum(val)" +
                        " FROM esms_integration WHERE type=?  AND cal_time BETWEEN ? AND ?  GROUP BY time ORDER BY time";
        //dtfdl和dtcdl 不需要做累计，直接使用最新数据
        if("dtfdl".equals(type) || "dtcdl".equals(type)){
            sql = "SELECT cal_time AS time, val" +
                    " FROM esms_integration WHERE type=?  AND cal_time BETWEEN ? AND ? ORDER BY time desc LIMIT 1 ";
        }else{
            //特殊处理，总充电量(zcdl)=当天充电量（dtcdl）之和
            if("zcdl".equals(type)){
                type = "dtcdl";
            }
            //特殊处理，总放电量(zfdl)=当天放电量（dtfdl）之和
            if("zfdl".equals(type)){
                type = "dtfdl";
            }
        }
        Query qry = entityManager.createNativeQuery(sql);
        qry.setParameter(1, type);
        qry.setParameter(2, startDate);
        qry.setParameter(3, endDate);
        List<Object[]> result = qry.getResultList();
        Double d = 0.0 ;
        for (Object[] rs : result) {
            d += Double.parseDouble(rs[1].toString())*0.00000001; //单位换算
            logger.info("pcs "+type+": "+rs[1] +"        :"+d);
        }
        return d;
    }


}
